﻿using System.CodeDom;
using System.Collections.Generic;
using InterfaceWeaver.AopCore;
using Roslyn.Compilers.CSharp;

namespace InterfaceWeaver.CustomTool
{
    public class CheckSetterHasChangesAspectHandler : AspectHandlerBase<CheckSetterHasChangesAttribute>
    {
        /// <summary>
        /// Regarding all the handler answering preview step, this step determines whether there are some aspect handler that should be removed from next live cycle.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="attributeSyntax">The attribute syntax.</param>
        /// <param name="indirectAspectHandler"> </param>
        /// <param name="indirectAttributeSyntax"> </param>
        /// <returns></returns>
        public override bool IsIndirectAspectsCompliantOnInterface(AspectDispatcher context, AttributeSyntax attributeSyntax, IAspectHandler indirectAspectHandler, AttributeSyntax indirectAttributeSyntax)
        {
            return !(indirectAspectHandler is MapDependencyPropertyAspectHandler);
        }

        /// <summary>
        /// Regarding all the handler answering preview step, this step determines whether there are some aspect handler that should be removed from next live cycle.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="attributeSyntax">The attribute syntax.</param>
        /// <param name="indirectAspectHandler"> </param>
        /// <param name="indirectAttributeSyntax"> </param>
        /// <returns></returns>
        public override bool IsIndirectAspectsCompliantOnProperty(AspectDispatcher context, AttributeSyntax attributeSyntax, IAspectHandler indirectAspectHandler, AttributeSyntax indirectAttributeSyntax)
        {
            return !(indirectAspectHandler is MapDependencyPropertyAspectHandler);
        }

        /// <summary>
        /// Handles the on member.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="attributeSyntax">The attribute syntax.</param>
        /// <param name="propertyDeclarationSyntax"> </param>
        /// <param name="handlerStreams"> </param>
        /// <returns>
        /// True if the aspect is handled, false otherwise
        /// </returns>
        public override void HandleOnProperty(AspectDispatcher context, AttributeSyntax attributeSyntax, PropertyDeclarationSyntax propertyDeclarationSyntax, IDictionary<PropertyDeclarationSyntax, AspectTuple[]> handlerStreams)
        {
            if (context.WeavedCodeContext.CurrentCodeMemberPropertyDeclaration.SetterCodeCoreStream != null)
            {
                var weavedPropertyContext = context.WeavedCodeContext.CurrentCodeMemberPropertyDeclaration;
                var codeFieldReference = new CodeFieldReferenceExpression(
                    new CodeThisReferenceExpression(),
                    weavedPropertyContext.CurrentCodeMemberFieldDeclaration.Name);
                var oldValueCheckCondition = new CodeBinaryOperatorExpression(
                        codeFieldReference,
                        CodeBinaryOperatorType.IdentityInequality,
                        new CodePropertySetValueReferenceExpression());

                // Creation of the test
                var oldValueCheckConditionStatment = new CodeConditionStatement(oldValueCheckCondition, new CodeStatement[0]);

                // The actual code in the setter is now in the true check statment
                oldValueCheckConditionStatment.TrueStatements.AddRange(weavedPropertyContext.CurrentCodeMemberPropertyDeclaration.SetStatements);
                
                // The setter is now replaced with the value check
                weavedPropertyContext.CurrentCodeMemberPropertyDeclaration.SetStatements.Clear();
                weavedPropertyContext.CurrentCodeMemberPropertyDeclaration.SetStatements.Add(oldValueCheckConditionStatment);

                // The body code stream is updated so that the body of the setter is now the true statment.
                weavedPropertyContext.UpdateSetterCodeStream(oldValueCheckConditionStatment.TrueStatements);
            }
        }
    }
}
